﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Web.UI.WebControls;
using System.Web.UI;
using Elmah;
using System.Collections.Specialized;
using System.Collections;
using System.Web;

namespace Our.Umbraco.Tree.Elmah
{
    class ElmahRender
    {
        public void RenderError(HtmlTextWriter writer, Error error, HttpServerUtility server)
        {


            //
            // Write out the page title containing error type and message.
            //

            writer.AddAttribute(HtmlTextWriterAttribute.Id, "PageTitle");
            writer.RenderBeginTag(HtmlTextWriterTag.H1);
            server.HtmlEncode(error.Message, writer);
            writer.RenderEndTag(); // </h1>
            writer.WriteLine();

            writer.AddAttribute(HtmlTextWriterAttribute.Id, "ErrorTitle");
            writer.RenderBeginTag(HtmlTextWriterTag.P);

            writer.AddAttribute(HtmlTextWriterAttribute.Id, "ErrorType");
            writer.RenderBeginTag(HtmlTextWriterTag.Span);
            server.HtmlEncode(error.Type, writer);
            writer.RenderEndTag(); // </span>

            writer.AddAttribute(HtmlTextWriterAttribute.Id, "ErrorTypeMessageSeparator");
            writer.RenderBeginTag(HtmlTextWriterTag.Span);
            writer.Write(": ");
            writer.RenderEndTag(); // </span>

            writer.AddAttribute(HtmlTextWriterAttribute.Id, "ErrorMessage");
            writer.RenderBeginTag(HtmlTextWriterTag.Span);
            server.HtmlEncode(error.Message, writer);
            writer.RenderEndTag(); // </span>

            writer.RenderEndTag(); // </p>
            writer.WriteLine();

            //
            // Do we have details, like the stack trace? If so, then write 
            // them out in a pre-formatted (pre) element. 
            // NOTE: There is an assumption here that detail will always
            // contain a stack trace. If it doesn't then pre-formatting 
            // might not be the right thing to do here.
            //

            if (error.Detail.Length != 0)
            {
                writer.AddAttribute(HtmlTextWriterAttribute.Id, "ErrorDetail");
                writer.RenderBeginTag(HtmlTextWriterTag.Pre);
                writer.Flush();
                server.HtmlEncode(error.Detail, writer.InnerWriter);
                writer.RenderEndTag(); // </pre>
                writer.WriteLine();
            }

            //
            // Write out the error log time. This will be in the local
            // time zone of the server. Would be a good idea to indicate
            // it here for the user.
            //

            writer.AddAttribute(HtmlTextWriterAttribute.Id, "ErrorLogTime");
            writer.RenderBeginTag(HtmlTextWriterTag.P);
            server.HtmlEncode(string.Format("Logged on {0} at {1}",
                error.Time.ToLongDateString(),
                error.Time.ToLongTimeString()), writer);
            writer.RenderEndTag(); // </p>
            writer.WriteLine();


            //
            // If this error has context, then write it out.
            // ServerVariables are good enough for most purposes, so
            // we only write those out at this time.
            //

            RenderCollection(writer, error.ServerVariables, "ServerVariables", "Server Variables", server);
        }

        private void RenderCollection(HtmlTextWriter writer, NameValueCollection collection, string id, string title, HttpServerUtility server)
        {
            //
            // If the collection isn't there or it's empty, then bail out.
            //

            if (collection == null || collection.Count == 0)
                return;

            //
            // Surround the entire section with a <div> element.
            //

            writer.AddAttribute(HtmlTextWriterAttribute.Id, id);
            writer.RenderBeginTag(HtmlTextWriterTag.Div);

            //
            // Write out the table caption.
            //

            writer.AddAttribute(HtmlTextWriterAttribute.Class, "table-caption");
            writer.RenderBeginTag(HtmlTextWriterTag.P);
            server.HtmlEncode(title, writer);
            writer.RenderEndTag(); // </p>
            writer.WriteLine();

            //
            // Some values can be large and add scroll bars to the page
            // as well as ruin some formatting. So we encapsulate the
            // table into a scrollable view that is controlled via the 
            // style sheet.
            //

            writer.AddAttribute(HtmlTextWriterAttribute.Class, "scroll-view");
            writer.RenderBeginTag(HtmlTextWriterTag.Div);

            //
            // Create a table to display the name/value pairs of the
            // collection in 2 columns.
            //

            Table table = new Table();
            table.CellSpacing = 0;

            //
            // Create the header row and columns.
            //

            TableRow headRow = new TableRow();

            TableHeaderCell headCell;

            headCell = new TableHeaderCell();
            headCell.Wrap = false;
            headCell.Text = "Name";
            headCell.CssClass = "name-col";

            headRow.Cells.Add(headCell);

            headCell = new TableHeaderCell();
            headCell.Wrap = false;
            headCell.Text = "Value";
            headCell.CssClass = "value-col";

            headRow.Cells.Add(headCell);

            table.Rows.Add(headRow);

            //
            // Create a row for each entry in the collection.
            //

            string[] keys = collection.AllKeys;
            LocalInvariantStringArray.Sort(keys);

            for (int keyIndex = 0; keyIndex < keys.Length; keyIndex++)
            {
                string key = keys[keyIndex];

                TableRow bodyRow = new TableRow();
                bodyRow.CssClass = keyIndex % 2 == 0 ? "even-row" : "odd-row";

                TableCell cell;

                //
                // Create the key column.
                //

                cell = new TableCell();
                cell.Text = server.HtmlEncode(key);
                cell.CssClass = "key-col";

                bodyRow.Cells.Add(cell);

                //
                // Create the value column.
                //

                cell = new TableCell();
                cell.Text = server.HtmlEncode(collection[key]);
                cell.CssClass = "value-col";

                bodyRow.Cells.Add(cell);

                table.Rows.Add(bodyRow);
            }

            //
            // Write out the table and close container tags.
            //

            table.RenderControl(writer);

            writer.RenderEndTag(); // </div>
            writer.WriteLine();

            writer.RenderEndTag(); // </div>
            writer.WriteLine();
        }

        public class LocalInvariantStringArray
        {
            public static void Sort(string[] keys)
            {
                Sort(keys, 0, keys.Length);
            }

            public static void Sort(string[] keys, int index, int length)
            {
                Sort(keys, null, index, length);
            }

            public static void Sort(string[] keys, Array items, int index, int length)
            {
                Array.Sort(keys, items, index, length, InvariantComparer);
            }

            private static IComparer InvariantComparer
            {
                get
                {
                    return Comparer.DefaultInvariant;
                }
            }


        }

    }
}
